<template>
  <li
    class="fa-drag-sort-item"
    :class="[
      {
        'is-hover': Dns.hoverIndex === index,
        'is-drag-active': Dns.activeIndex === index,
        'is-drag-over': Dns.overIndex === index,
        'is-disabled': Dns.disabled || disabled
      }
    ]"
    :draggable="isDraggable"
    @mousedown="mousedown"
    @mouseup="Dns.activeIndex = -1"
    @mouseenter="Dns.hoverIndex = index"
    @mouseleave="Dns.hoverIndex = -1"
    @dragstart="dragStart"
    @dragover.prevent="dragOver"
    @dragleave.prevent="dragLeave"
    @dragend="Dns.activeIndex = -1"
    @drop.prevent.stop="drop"
  >
    <a v-if="showHandle" :class="DEFAULT_HANDLE" class="fa-icon-drag-handle"></a>

    <div class="fa-drag-sort-item__content">
      <slot />
    </div>
  </li>
</template>

<script lang="ts">
import { computed, defineComponent, inject, ref } from 'vue'
import FaDragSort from './DragSort.vue'
import { isElMatchSelector, queryClosestSelector } from '@/utils'

export default defineComponent({
  name: 'FaDragSortItem',

  props: {
    /** 当前项索引 */
    index: {
      type: Number,
      required: true
    },

    /** 是否禁用选项拖拽排序 */
    disabled: {
      type: Boolean,
      default: false
    }
  },

  setup(props) {
    const Dns = inject('Dns') as InstanceType<typeof FaDragSort>

    /** 默认句柄 */
    const DEFAULT_HANDLE = 'fa-drag-sort-item__handle'

    /** 是否显示默认句柄 */
    const showHandle = computed(() => !Dns.disabled && Dns.handle === true)

    /** 是否拖拽句柄 */
    const isDragHandle = ref(true)

    /** 是否允许拖入 */
    const droppable = computed(() => !Dns.disabled && !props.disabled && Dns.activeIndex > -1)

    /** 是否允许拖拽 */
    const isDraggable = computed(() => !Dns.disabled && !props.disabled && isDragHandle.value)

    return {
      Dns: ref(Dns),

      /** 默认句柄 */
      DEFAULT_HANDLE,

      /** 是否显示默认句柄 */
      showHandle,

      /** 是否允许拖拽 */
      isDraggable,

      /** 鼠标按下 */
      mousedown(e: MouseEvent) {
        const target = e.target as HTMLElement

        let { handle } = Dns

        if (handle === true) handle = `.${DEFAULT_HANDLE}`

        // 点击区域如果不是句柄或者句柄子元素，则禁止拖拽
        if (
          handle &&
          typeof handle === 'string' &&
          !(isElMatchSelector(target, handle) || queryClosestSelector(target, handle))
        ) {
          isDragHandle.value = false

          document.addEventListener('mouseup', () => (isDragHandle.value = true))
          return
        }

        isDraggable.value && (Dns.activeIndex = props.index)
      },

      /** 拖拽开始 */
      dragStart(e: DragEvent) {
        const { index } = props
        const { dataTransfer } = e

        if (dataTransfer) {
          dataTransfer.effectAllowed = 'move'
        }

        Dns.activeIndex = index

        // 拖拽开始事件
        Dns.$emit('drag-start', {
          e,
          dragIndex: index,
          dragItem: Dns.list[index]
        })
      },

      /** 拖拽经过 */
      dragOver(e: DragEvent) {
        const { dataTransfer } = e

        if (dataTransfer) {
          dataTransfer.dropEffect = droppable.value ? 'move' : 'none'
        }

        if (droppable.value) {
          Dns.overIndex = props.index
        }
      },

      /** 拖拽离开 */
      dragLeave() {
        Dns.overIndex = -1
      },

      /** 释放后 */
      drop(e: DragEvent) {
        const { activeIndex, list } = Dns
        const { index } = props

        Dns.activeIndex = -1
        Dns.overIndex = -1

        /** 拖拽项 */
        const dragItem = list[activeIndex]
        /** 放入项 */
        const dropItem = list[index]

        if (activeIndex > -1) {
          list.splice(activeIndex, 1)
          list.splice(index, 0, dragItem)
        }

        // 拖入事件
        Dns.$emit('drop', {
          e,
          dragIndex: activeIndex,
          dragItem,
          dropIndex: index,
          dropItem
        })

        setTimeout(() => (Dns.hoverIndex = index), 310)
      }
    }
  }
})
</script>

<style lang="scss">
.fa-drag-sort-item {
  position: relative;
  display: flex;
  align-items: center;
  outline: 1px dashed transparent;
  transition: all 0.25s;

  &.is-drag-over {
    z-index: 4;
    background-color: $color-info-lightest;
    outline-color: $color-info-light;
  }

  &.is-drag-active {
    box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.1);
  }
}

.fa-drag-sort-item__handle {
  width: 24px;
  height: 24px;
  color: $color-info;
  font-size: 12px;
  line-height: 24px;
  text-align: center;
  cursor: move;

  .fa-drag-sort-item.is-drag-active & {
    color: $color-info-dark;
  }

  .fa-drag-sort-item.is-disabled & {
    color: $color-info-light;
    cursor: not-allowed;
  }
}

.fa-drag-sort-item__content {
  display: flex;
  flex: auto;
  align-items: center;
}
</style>
